#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <semaphore.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include <imp/imp_log.h>
#include <imp/imp_ivs.h>
#include <imp/imp_ivs_base_move.h>
#include <ivs_base_move.h>
#include <system/system.h>
#ifdef DMALLOC
#include <imp/dmalloc.h>
#endif
#define TAG "BASEIMPIVSMOVE"

#define NUM_SMG     6

uint32_t dump_base_move_ivs = 0;
#define DUMP_BIT(x) (dump_base_move_ivs&(1<<x))

static int BaseMoveInit(IMPIVSInterface *inf)
{
	 ivs_move_t *gmove = NULL;
	 if (!inf || !inf->param) {
		IMP_LOG_ERR(TAG, "%s:param is NULL!\n", __func__);
		goto err_param_null;
	}
	gmove = imp_alloc_base_move((IMP_IVS_BaseMoveParam *)inf->param, IMP_IVS_ReleaseData);
	if (gmove == NULL) {
		IMP_LOG_ERR(TAG, "imp_alloc_move() error !\n");
		goto err_imp_alloc_move;
	}
		gmove->movesem = calloc(1, sizeof(ivs_move_sem_t));
	if (NULL == gmove->movesem) {
		IMP_LOG_ERR(TAG, "calloc movesem error !\n");
		goto err_calloc_movesem;
	}

	gmove->movesem->result = calloc(NUM_SMG, sizeof(IMP_IVS_BaseMoveOutput));
	if (gmove->movesem->result == NULL) {
		IMP_LOG_ERR(TAG, "calloc() movesem->result error !\n");
		goto err_calloc_movesem_result;
	}
	int width = gmove->param.frameInfo.width;
	int height = gmove->param.frameInfo.height;
	int i = 0;
	for(i = 0; i < NUM_SMG; i++) {
		gmove->movesem->result[i].data = malloc(width*height/64);
	}

	gmove->movesem->index_write = 0;
	gmove->movesem->index_read = 0;

	inf->priv = gmove;

	return 0;

err_calloc_movesem_result:
	free(gmove->movesem);
err_calloc_movesem:
	imp_free_base_move(gmove);
err_imp_alloc_move:
	inf->priv = NULL;
err_param_null:
	return -1;


}
static void BaseMoveExit(IMPIVSInterface *inf)
{
	ivs_move_t *gmove = NULL;

	if (!inf || !inf->priv) {
		IMP_LOG_ERR(TAG, "%s:gmove is NULL !\n", __func__);
		goto err_gmove_null;
	}

	gmove = inf->priv;

	if(gmove->movesem == NULL) {
		IMP_LOG_ERR(TAG, "gmove->movesem is NULL !\n");
		goto err_movesem_null;
	}

	if(gmove->movesem->result == NULL) {
		IMP_LOG_ERR(TAG, "gmove->movesem->result is NULL !\n");
		goto err_movesem_result_null;
	}
	int i = 0;
	for (i = 0; i < NUM_SMG; i++) {
		free(gmove->movesem->result[i].data);
	}


	free(gmove->movesem->result);
	free(gmove->movesem);
	imp_free_base_move(gmove);

	inf->priv = NULL;

	return;

err_movesem_result_null:
	free(gmove->movesem);
err_movesem_null:
	imp_free_base_move(gmove);
	inf->priv = NULL;
err_gmove_null:
	return;
}

static int BaseMovePreprocessSync(IMPIVSInterface *inf, IMPFrameInfo *frame)
{
	if (!inf || !inf->priv) {
		IMP_LOG_ERR(TAG, "%s:gmove is NULL!\n", __func__);
		return -1;
	}

	return imp_base_move_preprocess(inf->priv, frame);
}

static int BaseMoveProcessAsync(IMPIVSInterface *inf, IMPFrameInfo *frame)
{
	int id, ret;

	ivs_move_t *gmove = NULL;

#ifdef TEST_BUF
	int fd;

	fd = open("./yuyv", O_RDWR | O_CREAT | O_APPEND, 0777);
	if(fd < 0) {
		IMP_LOG_ERR(TAG, "fd error !\n");
		return -1;
	}

	write(fd, (void *)frame->virAddr, frame->width * frame->height);

	close(fd);
#endif

	if (!inf || !inf->priv) {
		IMP_LOG_ERR(TAG, "%s:gmove is NULL!\n", __func__);
		return -1;
	}

	gmove = (ivs_move_t *)inf->priv;

	id = gmove->movesem->index_write;

	ret = imp_base_move_process(gmove, frame, &(gmove->movesem->result[id]));

	if (DUMP_BIT(0))
	{
		int i = 0;
		IMP_IVS_BaseMoveOutput *result = &(gmove->movesem->result[id]);
		if (0 == id) {
			//printf("result: write id = %d, ret = %d, datalen = %d ret = %d\n", id, result->ret, result->datalen, ret);
			IMP_LOG_ERR(TAG, "result: write id = %d, ret = %d, datalen = %d ret = %d\n", id, result->ret, result->datalen, ret);
		}
		uint8_t *data = result->data;
		uint32_t cnt = 0;
		for (i = 0; i < result->datalen; i++) {
			if (0 != data[i]) cnt++;
		}
		if (0 != cnt) {
			//printf("result: write cnt = %d\n", cnt);
			IMP_LOG_ERR(TAG, "result: write cnt = %d\n", cnt);
		}

	}
	if (ret == 0) {
		gmove->movesem->index_write = ((gmove->movesem->index_write + 1) % NUM_SMG);
	} else if (ret < 0) {
		IMP_LOG_ERR(TAG, "imp_move_process() error !\n");
	}

	return ret;
}

static int BaseMoveGetResult(IMPIVSInterface *inf, void **result)
{
	int id;
	ivs_move_t *gmove = NULL;

	if (!inf || !inf->priv) {
		IMP_LOG_ERR(TAG, "%s:gmove is NULL !\n", __func__);
		return -1;
	}

	gmove = (ivs_move_t *)inf->priv;

	id = gmove->movesem->index_read;

	*result = &(gmove->movesem->result[id]);

	if (DUMP_BIT(0))
	{
		int i = 0;
		IMP_IVS_BaseMoveOutput *result = &(gmove->movesem->result[id]);
		//printf("result: read id = %d, ret = %d, datalen = %d\n", id, result->ret, result->datalen);
		IMP_LOG_ERR(TAG, "result: read id = %d, ret = %d, datalen = %d\n", id, result->ret, result->datalen);
		uint8_t *data = result->data;
		uint32_t cnt = 0;
		for (i = 0; i < result->datalen; i++) {
			if (0 != data[i]) cnt++;
		}
		//printf("result: read cnt = %d\n", cnt);
		IMP_LOG_ERR(TAG, "result: read cnt = %d\n", cnt);

	}
	gmove->movesem->index_read = ((gmove->movesem->index_read + 1) % NUM_SMG);

	return 0;
}

static int BaseMoveReleaseResult(IMPIVSInterface *inf, void *result)
{
	return 0;
}

static int BaseMoveGetParam(IMPIVSInterface *inf, void *param)
{
	if (!inf || !inf->priv) {
		IMP_LOG_ERR(TAG, "%s:gmove is NULL !\n", __func__);
		return -1;
	}

	return imp_get_base_move_param(inf->priv, param);
}

static int BaseMoveSetParam(IMPIVSInterface *inf, void *param)
{
	if (!inf || !inf->priv) {
		IMP_LOG_ERR(TAG, "%s:gmove is NULL !\n", __func__);
		return -1;
	}

	return imp_set_base_move_param(inf->priv, param);
}

static int BaseMoveFlushFrame(IMPIVSInterface *inf)
{
	if (!inf || !inf->priv) {
		IMP_LOG_ERR(TAG, "%s:gmove is NULL !\n", __func__);
		return -1;
	}

	return imp_base_flush_frame(inf->priv);
}



IMPIVSInterface *IMP_IVS_CreateBaseMoveInterface(IMP_IVS_BaseMoveParam *param)
{
	IMPIVSInterface *baseMoveInterface = calloc(1, sizeof(IMPIVSInterface) + sizeof(IMP_IVS_BaseMoveParam));
	if (NULL == baseMoveInterface) {
		IMP_LOG_ERR(TAG, "calloc baseMoveInterface is NULL!\n");
		return NULL;
	}

	baseMoveInterface->param = (char*)baseMoveInterface + sizeof(IMPIVSInterface);
	memcpy(baseMoveInterface->param, param, sizeof(IMP_IVS_BaseMoveParam));
	baseMoveInterface->paramSize = sizeof(IMP_IVS_BaseMoveParam);
	baseMoveInterface->pixfmt = PIX_FMT_NV12;
	baseMoveInterface->init = BaseMoveInit;
	baseMoveInterface->exit = BaseMoveExit;
	baseMoveInterface->preProcessSync = BaseMovePreprocessSync;

	baseMoveInterface->processAsync = BaseMoveProcessAsync;
	baseMoveInterface->getResult    = BaseMoveGetResult;
	baseMoveInterface->releaseResult= BaseMoveReleaseResult;
	baseMoveInterface->getParam     = BaseMoveGetParam;
	baseMoveInterface->setParam     = BaseMoveSetParam;
	baseMoveInterface->flushFrame   = BaseMoveFlushFrame;

	return baseMoveInterface;
}

void IMP_IVS_DestroyBaseMoveInterface(IMPIVSInterface *baseMoveInterface)
{
	if (NULL != baseMoveInterface) {
		free(baseMoveInterface);
	}
}
